package com.dd.cloud.user.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.dd.cloud.common.config.common.ProfilesProperties;
import com.dd.cloud.common.constants.HealthConstants;
import com.dd.cloud.common.constants.ZekeConstants;
import com.dd.cloud.common.exception.ZekeException;
import com.dd.cloud.common.utils.HttpRequestUtils;
import com.dd.cloud.user.res.patient.PatientBindDocRes;
import com.dd.cloud.user.service.patient.IPatientUserService;
import com.dd.cloud.user.vo.Article;
import com.dd.cloud.user.vo.NewsMessage;
import com.dd.cloud.user.vo.TextMessage;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HTTP;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class WxUtil {
    @Autowired
    private ProfilesProperties profilesProperties;
    @Autowired
    private IPatientUserService patientUserService;
    /**
     * 获取公众号二维码的ticket，再通过ticket去换二维码
     * https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
     */
    public static final String GET_QRCODE_TICKET = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=";
    /**
     * 通过ticket换取公众号二维码
     */
    public static final String GET_QRCODE_BY_TICKET = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=";
    private static final String PATH = "imgTemp";

    //测试专用
    public static MultipartFile getQRCode(String scene, String accessToken, String page, String active) {
        Map<String, Object> params = new HashMap<>();
        //参数,一个可以直接放入参数例如:1 多个例如：id=1&name=2&...
        params.put("scene", scene);
        //扫码后进入小程序的页面位置
        params.put("page", page);
        if (ZekeConstants.ProfilesType.DEV.equals(active) || ZekeConstants.ProfilesType.TEST.equals(active)) {
            params.put("env_version", "trial");
            params.put("check_path", false);
        }
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        // 接口
        HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken);
        httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
        httpPost.setHeader("Accept", "application/json");
        //必须是json模式的 post
        String body = JSON.toJSONString(params);
        log.info(JSON.toJSONString(body));
        StringEntity entity = null;
        try {
            entity = new StringEntity(body, StandardCharsets.UTF_8);
            entity.setContentEncoding("UTF-8");
            //也可以是image/jpg
            entity.setContentType("image/png");
            httpPost.setEntity(entity);
            log.info(JSON.toJSONString(httpPost));
            HttpResponse response;
            response = httpClient.execute(httpPost);
            InputStream inputStream = response.getEntity().getContent();
            String path = PATH + File.separator;
            String fname = "img-doctor-qr-" + System.currentTimeMillis() + ".png";
            File outFile = new File(path + fname);
            String filePath = path + fname;
            System.out.println(filePath);
            if (!outFile.getParentFile().exists()) {
                outFile.getParentFile().mkdirs();
            }
            FileOutputStream fos = new FileOutputStream(outFile);
            byte[] b = new byte[1024];
            int nRead = 0;
            while ((nRead = inputStream.read(b)) != -1) {
                fos.write(b, 0, nRead);
            }
            fos.flush();
            fos.close();
            FileInputStream fileInputStream = new FileInputStream(filePath);
            MultipartFile multipartFile = new MockMultipartFile(outFile.getName(), outFile.getName(), ContentType.IMAGE_PNG.toString(), fileInputStream);
            outFile.delete();
            outFile.getParentFile().delete();
            return multipartFile;
        } catch (Exception e) {
            log.info(JSON.toJSONString(e));
            throw new ZekeException("生成二维码失败");
        }
    }


    public static Map<String, String> getWxMiniPhone(String sessionkey, String iv, String encryptedData) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
        byte[] encrypData = Base64Utils.decodeFromString(encryptedData);
        byte[] ivData = Base64Utils.decodeFromString(iv);
        byte[] sessionKey = Base64Utils.decodeFromString(sessionkey);
        String resultString = null;
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
        SecretKeySpec keySpec = new SecretKeySpec(sessionKey, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        resultString = new String(cipher.doFinal(encrypData), StandardCharsets.UTF_8);
        Map<String, String> object = JSONObject.parseObject(resultString, Map.class);
        // 拿到手机号码
        //String phone = object.getString("phoneNumber");
        return object;
    }

    /**
     * 获取服务号二维码
     *
     * @param wxQrCodeParam 二维码自定义参数
     * @param accessToken   授权token
     * @param isPermanent   是否是永久二维码
     * @return
     */
    public static String createGuidanceQrCode(WxQrCodeParam wxQrCodeParam, String accessToken, Boolean isPermanent) {
        CreateQrCodeReq req = new CreateQrCodeReq();
        if (isPermanent) {
            req.setActionName(ActionNameEnum.QR_LIMIT_STR_SCENE.name());
        } else {
            /*
              30天有效期  设置过期时间为临时码并且设置二维码类型为临时码
              二维码类型，QR_SCENE为临时的整型参数值，QR_STR_SCENE为临时的字符串参数值
             */
            req.setExpireSeconds(2592000);
            req.setActionName(ActionNameEnum.QR_STR_SCENE.name());
        }
        req.setActionInfo(new Scene(new SceneParam().setSceneStr(JSON.toJSONString(wxQrCodeParam))));
        String url = GET_QRCODE_TICKET + accessToken;
        try {
            //获取二维码ticket
            String res = HttpRequestUtils.postWithJson(url, JSON.toJSONString(req));
            log.info("获取二维码ticket请求响应详情:{}", JSON.toJSONString(res));
            //通过ticket换取二维码
            String ticket = (String) JSON.parseObject(res, Map.class).get("ticket");
            return GET_QRCODE_BY_TICKET + ticket;
        } catch (IOException e) {
            e.printStackTrace();
            throw new ZekeException("调用维微信API创建二维码ticket失败");
        }
    }

    /**
     * @author Lx
     * @version 1.0
     * @date 2023/10/11
     * 创建服务号二维码
     * 二维码类型，QR_SCENE为临时的整型参数值，QR_STR_SCENE为临时的字符串参数值，QR_LIMIT_SCENE为永久的整型参数值，QR_LIMIT_STR_SCENE为永久的字符串参数值
     */
    public enum ActionNameEnum {
        QR_SCENE, QR_STR_SCENE, QR_LIMIT_SCENE, QR_LIMIT_STR_SCENE
    }

    /**
     * @author Lx
     * @version 1.0
     * @date 2023/10/11
     */
    @Data
    public static class WxQrCodeParam {
        /**
         * 二维码类型 1扫码填写就诊卡
         */
        private Integer type;

        /**
         * 医生ID
         */
        private Integer doctorId;

        /**
         * 参数
         */
        private String param;
    }

    /**
     * @author Lx
     * @version 1.0
     * @date 2023/10/11
     */
    @Data
    @AllArgsConstructor
    public static class Scene {
        @JSONField(name = "scene")
        @JsonProperty(value = "scene")
        private SceneParam sceneParam;
    }

    /**
     * 二维码自定义参数
     *
     * @author Lx
     * @version 1.0
     * @date 2023/10/11
     */
    @Data
    @Accessors(chain = true)
    public static class SceneParam {
        /**
         * 场景值ID，临时二维码时为32位非0整型，永久二维码时最大值为100000（目前参数只支持1--100000）
         */
        @JSONField(name = "scene_id")
        @JsonProperty(value = "scene_id")
        private Integer sceneId;

        /**
         * 场景值ID（字符串形式的ID），字符串类型，长度限制为1到64
         */
        @JSONField(name = "scene_str")
        @JsonProperty(value = "scene_str")
        private String sceneStr;
    }

    /**
     * @author Lx
     * @version 1.0
     * @date 2023/10/11
     * 获取公众号二维码请求参数
     */
    @Data
    @Accessors(chain = true)
    public static class CreateQrCodeReq {
        /**
         * 有效时间（秒）
         */
        @JSONField(name = "expire_seconds")
        @JsonProperty(value = "expire_seconds")
        private Integer expireSeconds;

        /**
         * 二维码类型，QR_SCENE为临时的整型参数值，QR_STR_SCENE为临时的字符串参数值，QR_LIMIT_SCENE为永久的整型参数值，QR_LIMIT_STR_SCENE为永久的字符串参数值
         */
        @JSONField(name = "action_name")
        @JsonProperty(value = "action_name")
        private String actionName;

        /**
         * 二维码详细信息
         */
        @JSONField(name = "action_info")
        @JsonProperty(value = "action_info")
        private Scene actionInfo;
    }

    /**
     * 返回公众号图文消息 拼接xml
     */
    public static String getXmlString2(NewsMessage newMessage) {
        String xml = "";
        if (newMessage != null) {
            List<Article> articles = newMessage.getArticles();
            xml = "<xml>";
            xml += "<ToUserName><![CDATA[";
            xml += newMessage.getToUserName();
            xml += "]]></ToUserName>";
            xml += "<FromUserName><![CDATA[";
            xml += newMessage.getFromUserName();
            xml += "]]></FromUserName>";
            xml += "<CreateTime>";
            xml += newMessage.getCreateTime();
            xml += "</CreateTime>";
            xml += "<MsgType><![CDATA[";
            xml += newMessage.getMsgType();
            xml += "]]></MsgType>";
            xml += "<ArticleCount>";
            xml += newMessage.getArticleCount();
            xml += "</ArticleCount>";
            xml += "<Articles>";
            for (Article article : articles) {
                xml += "<item>";
                xml += "<Title><![CDATA[";
                xml += article.getTitle();
                xml += "]]></Title>";
                xml += "<Description><![CDATA[";
                xml += article.getDescription();
                xml += "]]></Description>";
                xml += "<PicUrl><![CDATA[";
                xml += article.getPicUrl();
                xml += "]]></PicUrl>";
                xml += "<Url><![CDATA[";
                xml += article.getUrl();
                xml += "]]></Url>";
                xml += "</item>";
            }
            xml += "</Articles>";
            xml += "</xml>";
        }
        return xml;
    }

    /**
     * 返回公众号文本消息 拼接xml
     */
    public static String getXmlString1(TextMessage textMessage) {
        String xml = "";
        if (textMessage != null) {
            xml = "<xml>";
            xml += "<ToUserName><![CDATA[";
            xml += textMessage.getToUserName();
            xml += "]]></ToUserName>";
            xml += "<FromUserName><![CDATA[";
            xml += textMessage.getFromUserName();
            xml += "]]></FromUserName>";
            xml += "<CreateTime>";
            xml += textMessage.getCreateTime();
            xml += "</CreateTime>";
            xml += "<MsgType><![CDATA[";
            xml += textMessage.getMsgType();
            xml += "]]></MsgType>";
            xml += "<Content>";
            xml += textMessage.getContent();
            xml += "</Content>";
            xml += "<MsgId>";
            xml += textMessage.getMsgId();
            xml += "</MsgId>";
            xml += "</xml>";
        }
        return xml;
    }

    /**
     * @param decryptMap: 二维码参数
     * @param eventKey:   消息参数
     * @return String xml信息
     * @description 推送消息给用户
     * @author czg
     * @date 2023/10/12 11:10
     */
    public String getReturnMessage1(Map<String, String> decryptMap, String eventKey) throws Exception {
        log.error("decryptMap:" + decryptMap.toString());
        log.error("eventKey:" + eventKey);
        //扫码用户的openid
        String openid = decryptMap.get("FromUserName");
        if (eventKey != null && !"".equals(eventKey)) {
            if (eventKey.contains("qrscene_")) {
                eventKey = eventKey.replace("qrscene_", "");
            }
            WxQrCodeParam wxQrCodeParam = JSON.parseObject(eventKey, WxQrCodeParam.class);
            Map<String, Object> map;
            if (StringUtils.isBlank(wxQrCodeParam.getParam())) {
                map = new HashMap<>();
            } else {
                map = JSON.parseObject(wxQrCodeParam.getParam(), Map.class);
            }
            log.info("wxQrCodeParam:{},map:{}", wxQrCodeParam, map);
            if (wxQrCodeParam.getType() == 1) {
                //返回xml内容
                String returnXml = null;
                Boolean aBoolean = patientUserService.queryIsBindDoc(openid, wxQrCodeParam.getDoctorId());
                if (aBoolean == null) {
                    //TODO 即没有就诊卡,也没绑定医生
                    String path;
                    //点击跳转的地址
                    if (profilesProperties.getActive().equals(HealthConstants.ProfilesType.PROD)) {
                        path = "https://admin.dadaotcm.com/dadaomob/pages/patientInfoSpecial/patientInfoSpecial?index=1&doctorId=" + wxQrCodeParam.getDoctorId() + "&isCertification=" + map.get("isCertification") + "&openid=" + openid;
                    } else {
                        path = "https://admin.dadaotcm.com/dadaomos/pages/patientInfoSpecial/patientInfoSpecial?index=1&doctorId=" + wxQrCodeParam.getDoctorId() + "&isCertification=" + map.get("isCertification") + "&openid=" + openid;
                    }
                    Article article1 = new Article();
                    article1.setDescription(map.get("name") + "邀请你填写就诊卡");
                    //显示的图片
                    article1.setPicUrl("https://zk-ak.obs.cn-south-1.myhuaweicloud.com:443/dadao%2F%E6%82%A3%E8%80%85%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E5%9B%9E%E5%A4%8D%E5%9B%BE%E7%89%87.png");
                    article1.setTitle("点击填写就诊卡");
                    article1.setUrl(path);
                    List<Article> list = new ArrayList<>();
                    list.add(article1);
                    NewsMessage newMessage = new NewsMessage();
                    newMessage.setArticleCount(1);
                    newMessage.setCreateTime(System.currentTimeMillis());
                    newMessage.setFromUserName(decryptMap.get("ToUserName"));
                    newMessage.setToUserName(decryptMap.get("FromUserName"));
                    newMessage.setMsgType("news");
                    newMessage.setArticles(list);
                    newMessage.setFuncFlag(1);
                    returnXml = getXmlString2(newMessage);
                    log.info("即没有就诊卡,也没绑定医生");
                }
                if (Boolean.TRUE.equals(aBoolean)) {
                    //TODO 有就诊卡并已绑定医生
                    TextMessage textMessage = new TextMessage();
                    textMessage.setCreateTime(System.currentTimeMillis());
                    textMessage.setFromUserName(decryptMap.get("ToUserName"));
                    textMessage.setToUserName(decryptMap.get("FromUserName"));
                    textMessage.setMsgType("text");
                    textMessage.setContent("您已加入" + map.get("name") + "医生诊室");
                    returnXml = getXmlString1(textMessage);
                    log.info("有就诊卡并已绑定医生");
                }
                if (Boolean.FALSE.equals(aBoolean)) {
                    PatientBindDocRes res = patientUserService.queryBindDoc(openid, wxQrCodeParam.getDoctorId());
                    //TODO 有就诊卡但没绑定医生
                    String path;
                    //点击跳转的地址
                    if (profilesProperties.getActive().equals(HealthConstants.ProfilesType.PROD)) {
                        path = "https://admin.dadaotcm.com/dadaomo/pages/bindDoctor/bindDoctor?doctorId=" + wxQrCodeParam.getDoctorId() + "&medId=" + res.getMedId() + "&name=" + map.get("name");
                    } else {
                        path = "https://admin.dadaotcm.com/dadaomos/pages/bindDoctor/bindDoctor?doctorId=" + wxQrCodeParam.getDoctorId() + "&medId=" + res.getMedId() + "&name=" + map.get("name");
                    }
                    Article article1 = new Article();
                    article1.setDescription(map.get("name") + "邀请您加入");
                    //显示的图片
                    article1.setPicUrl("https://zk-ak.obs.cn-south-1.myhuaweicloud.com:443/dadao%2F%E6%82%A3%E8%80%85%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E5%9B%9E%E5%A4%8D%E5%9B%BE%E7%89%87.png");
                    article1.setTitle("请加入我的诊室");
                    article1.setUrl(path);
                    List<Article> list = new ArrayList<>();
                    list.add(article1);
                    NewsMessage newMessage = new NewsMessage();
                    newMessage.setArticleCount(1);
                    newMessage.setCreateTime(System.currentTimeMillis());
                    newMessage.setFromUserName(decryptMap.get("ToUserName"));
                    newMessage.setToUserName(decryptMap.get("FromUserName"));
                    newMessage.setMsgType("news");
                    newMessage.setArticles(list);
                    newMessage.setFuncFlag(1);
                    returnXml = getXmlString2(newMessage);
                    log.info("有就诊卡但没绑定医生");
                }
                log.error(returnXml);
                return returnXml;
            }
        } else {
            //此时有情况是关注事件, 记录用户信息
            patientUserService.queryIsBindDoc(openid, null);
        }
        return null;
    }
}